﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;
using Mbs.Threading;
using System.Linq;

namespace Mbs.Collections
{
    public interface IMap<TKey, TValue> : IDictionary<TKey, TValue>,IDisposable
    {
        //int Count { get; }
        //TValue this[TKey key] { get;set;}
        //ICollection<TKey> Keys { get; }
        //ICollection<TValue> Values { get; }

        //void Add(TKey key,TValue value);
        //TValue Get(TKey key);
        //bool TryGetValue(TKey key, out TValue value);
        TValue GetOrAdd(TKey key, TValue value);
        TValue GetOrAdd(TKey key, Func<TValue> creator);
        //bool Remove(TKey key);
        //bool ContainsKey(TKey key);
        //void Clear();
    }

    internal class Map<TKey, TValue> : DisposableObject, IMap<TKey, TValue>
    {
        private Dictionary<TKey, TValue> Inner;
        public Map()
        {
            Inner = new Dictionary<TKey, TValue>();
        }
       
        public Map(int capacity)
        {
            Inner = new Dictionary<TKey, TValue>(capacity);
        }

        public Map(IEqualityComparer<TKey> comparer)
        {
            Inner = new Dictionary<TKey, TValue>(comparer);
        }
        public Map(int capacity, IEqualityComparer<TKey> comparer)
        {
            Inner = new Dictionary<TKey, TValue>(capacity, comparer);
        }

        public TValue this[TKey key]
        {
            get { return Get(key);}
            set{ Add(key,value);}
        }

        public ICollection<TKey> Keys { get { return Inner.Keys; } }
        public ICollection<TValue> Values { get { return Inner.Values; } }

        public bool ContainsKey(TKey key)
        {
            return Inner.ContainsKey(key);
        }

        public void Add(TKey key, TValue value)
        {
            CheckNotDisposed();

            if (key != null)
            {
                Inner.Add(key, value);
            }
        }

        public TValue Get(TKey key)
        {
            CheckNotDisposed();

            TValue result = default(TValue);
            if (key != null)
                Inner.TryGetValue(key, out result);
                   
            return result;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            if (Inner.TryGetValue(key, out value))
                return true;

            value = default(TValue);

            return false;
        }

        public TValue GetOrAdd(TKey key, Func<TValue> creator)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            TValue value = default(TValue);
            if (Inner.TryGetValue(key, out value))
                return value;

            if (creator != null)
            {
                value = creator();
                if (value == null)
                    return value;

                Inner.Add(key, value);
                return value;
            }

            return default(TValue);
        }

        public TValue GetOrAdd(TKey key, TValue value)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            if (Inner.TryGetValue(key, out value))
                return value;
            Inner.Add(key, value);

            return value;
        }

        public bool Remove(TKey key)
        {
            CheckNotDisposed();

            if (Inner.ContainsKey(key))
                return Inner.Remove(key);
                    
            return false;
        }

        public void Clear()
        {
            CheckNotDisposed();

            Inner.Clear();
               
        }

        public int Count
        {
            get
            {
                CheckNotDisposed();

                return Inner.Count;
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            CheckNotDisposed();

            return Inner.GetEnumerator();
        }

        public bool IsReadOnly { get { return false; } }

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            Inner.Clear();
        }


        #region ICollection<KeyValuePair<TKey,TValue>> Members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return ContainsKey(item.Key);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            (Inner as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }

        #endregion
    }

    [Serializable]
    internal class ConcurrentMap<TKey, TValue> : DisposableObject, IMap<TKey, TValue>
    {
        private Dictionary<TKey, TValue> Inner;
        public ConcurrentMap()
        {
            Inner = new Dictionary<TKey, TValue>();
        }
       
        public ConcurrentMap(int capacity)
        {
            Inner = new Dictionary<TKey, TValue>(capacity);
        }

        public ConcurrentMap(IEqualityComparer<TKey> comparer)
        {
            Inner = new Dictionary<TKey, TValue>(comparer);
        }
        public ConcurrentMap(int capacity, IEqualityComparer<TKey> comparer)
        {
            Inner = new Dictionary<TKey, TValue>(capacity, comparer);
        }

        public bool IsReadOnly { get { return false; } }

        private readonly object locker = new object();
        private ReaderWriterLockSlim _Mutex;
        private ReaderWriterLockSlim Mutex
        {
            get
            {
                if (_Mutex == null)
                {
                    lock (locker)
                    {
                        if(_Mutex == null)
                            _Mutex = new ReaderWriterLockSlim();
                    }
                }
                return _Mutex;
            }
        }

        public bool ContainsKey(TKey key)
        {
            return Inner.ContainsKey(key);
        }

        public TValue this[TKey key]
        {
            get { return Get(key);}
            set{ Add(key,value);}
        }

        public ICollection<TKey> Keys { get { return Inner.Keys; } }
        public ICollection<TValue> Values { get { return Inner.Values; } }

        public void Add(TKey key, TValue value)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            using (new WriterLock(Mutex))
            {
                Inner.Add(key, value);
            }
        }

        public TValue Get(TKey key)
        {
            CheckNotDisposed();
           
            if (key == null)
                throw new ArgumentNullException("key");

            TValue result = default(TValue);
            using (new ReaderLock(Mutex))
                Inner.TryGetValue(key, out result);
            return result;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            using (new ReaderLock(Mutex))
                if (Inner.TryGetValue(key, out value))
                    return true;

            value = default(TValue);

            return false;
        }


        public TValue GetOrAdd(TKey key, TValue value)
        {
            CheckNotDisposed();

            if (key == null)
                throw new ArgumentNullException("key");

            using (new UpgradeLock(Mutex))
            {
                if (!Inner.ContainsKey(key))
                {
                    using (new WriterLock(Mutex))
                        Inner.Add(key, value);
                }
                else
                {
                    using (new ReaderLock(Mutex))
                        value = Inner[key];
                }
            }

            return value;
        }

        public TValue GetOrAdd(TKey key, Func<TValue> creator)
        {
            CheckNotDisposed();

            TValue value = default(TValue);
            if (key == null)
                throw new ArgumentNullException("key");

            using (new UpgradeLock(Mutex))
            {
                if (!Inner.ContainsKey(key))
                {
                    using (new WriterLock(Mutex))
                    {
                        if (creator != null)
                            value = creator();
                        Inner.Add(key, value);
                    }
                }
                else
                {
                    using (new ReaderLock(Mutex))
                        value = Inner[key];
                }
            }

             return value;
        }

        public bool Remove(TKey key)
        {
            CheckNotDisposed();
            if (key == null)
                throw new ArgumentNullException("key");

            if (Inner.ContainsKey(key))
                using (new WriterLock(Mutex))
                    return Inner.Remove(key);
            return false;
        }

        public void Clear()
        {
            CheckNotDisposed();

            if(Inner != null)
                using (new WriterLock(Mutex))
                    Inner.Clear();
        }

        public int Count
        {
            get
            {
                CheckNotDisposed();

                return Inner.Count;
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            CheckNotDisposed();

            return new Dictionary<TKey, TValue>(Inner).GetEnumerator();
        }

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            Add(item.Key, item.Value);
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return ContainsKey(item.Key);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            (Inner as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return Remove(item.Key);
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            if (_Mutex != null)
            {
                _Mutex.Dispose();
                _Mutex = null;
            }
            if (Inner != null)
            {
                Inner.Clear();
                Inner = null;
            }
        }

       
    }
}
